package com.qqt.csr.route.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qqt.csr.common.acl.aics.AICustomerServiceClient;
import com.qqt.csr.common.acl.aics.resp.ChannelInfoDTO;
import com.qqt.csr.common.acl.aics.resp.CsMemberInfoDTO;
import com.qqt.csr.common.enums.YesOrNotEnum;
import com.qqt.csr.common.exception.StatusCode;
import com.qqt.csr.common.utils.CacheKeyUtil;
import com.qqt.csr.common.utils.JsonUtil;
import com.qqt.csr.common.utils.ServiceAssert;
import com.qqt.csr.common.vo.req.PageRequest;
import com.qqt.csr.common.vo.resp.PageResponse;
import com.qqt.csr.route.assembler.RouteDistributionRuleAssembler;
import com.qqt.csr.route.entity.RouteBizRule;
import com.qqt.csr.route.entity.RouteDistributionRule;
import com.qqt.csr.route.entity.ServiceTime;
import com.qqt.csr.route.mapper.RouteDistributionRuleMapper;
import com.qqt.csr.route.vo.req.*;
import com.qqt.csr.route.vo.resp.RouteDistributionRuleDetailRespVO;
import com.qqt.csr.route.vo.resp.RouteRulePageRespVO;
import com.qqt.csr.workbench.entity.SkillSet;
import com.qqt.csr.workbench.service.SkillSetService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class RouteDistributionRuleService extends ServiceImpl<RouteDistributionRuleMapper, RouteDistributionRule> {
    @Autowired
    private RouteBizRuleService routeBizRuleService;
    @Autowired
    private ServiceTimeService serviceTimeService;
    @Autowired
    private SkillSetService skillSetService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private AICustomerServiceClient customerServiceClient;

    /**
     * 用于前端查询路由规则下拉列表
     * @param tenantId
     * @return
     */
    public List<RouteDistributionRule> queryList(String tenantId) {
        return this.lambdaQuery().select(RouteDistributionRule::getId,RouteDistributionRule::getName)
                .eq(RouteDistributionRule::getTenantId, tenantId).list();
    }
    /**
     * 根据id查询数据
     *
     * @param id
     * @return
     */
    public RouteDistributionRule queryById(Integer id) {
        String cacheKey = String.format(CacheKeyUtil.CsrRoute.ROUTE_INFO, id);
        return Optional.ofNullable(redissonClient.getBucket(cacheKey).get())
                .map(Object::toString)
                .map(json -> JsonUtil.toObject(json, RouteDistributionRule.class))
                .orElseGet(() -> {
                    RouteDistributionRule rule = this.lambdaQuery().select().eq(RouteDistributionRule::getId, id).one();
                    if (rule == null) {
                        return null;
                    }
                    List<RouteBizRule> routeBizRules = routeBizRuleService.queryByRuleId(rule.getId());
                    rule.setRouteBizRuleList(routeBizRules);
                    redissonClient.getBucket(cacheKey).set(JsonUtil.toJson(rule), Duration.of(CacheKeyUtil.EXPIRE_TIME_8H, ChronoUnit.MILLIS));
                    return rule;
                });
    }

    /**
     * 创建路由
     *
     * @param tenantId
     * @param creatorId
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Throwable.class)
    public Integer add(String tenantId, Long creatorId, AddingRouteDistributionRuleReqVO req) {
        List<RouteDistributionRule> existSameNameList = this.lambdaQuery().select(RouteDistributionRule::getId)
                .eq(RouteDistributionRule::getName, req.getName()).eq(RouteDistributionRule::getTenantId, tenantId).list();
        ServiceAssert.isTrue(CollectionUtils.isEmpty(existSameNameList), StatusCode.Common.UNIQUE_ERROR.getCode(), "已存在相同名称的路由");

        // 技能组校验
        checkSkillSetExist(tenantId, req.getRouteBizRuleList(), req.getQueueTimeoutCfg());

        // 服务时间组校验
        checkServiceTimeExist(tenantId, req.getBizTimeId(), req.getRouteBizRuleList());

        RouteDistributionRule rule = RouteDistributionRuleAssembler.INSTANCE.toRouteDistributionRule(req);
        rule.setTenantId(tenantId);
        rule.setCreatorId(creatorId);
        this.save(rule);

        List<RouteBizRule> routeBizRuleList = RouteDistributionRuleAssembler.INSTANCE.toRouteBizRuleList(req.getRouteBizRuleList());
        routeBizRuleList.forEach(bizRule -> {
            bizRule.setRuleId(rule.getId());
        });
        routeBizRuleService.batchInsert(routeBizRuleList);

        return rule.getId();
    }

    /**
     * 修改路由
     *
     * @param tenantId
     * @param req
     */
    @Transactional(rollbackFor = Throwable.class)
    public void updateBy(String tenantId, UpdatingRouteDistributionRuleReqVO req) {
        RouteDistributionRule existRule = this.lambdaQuery().select(RouteDistributionRule::getId)
                .eq(RouteDistributionRule::getId, req.getId()).eq(RouteDistributionRule::getTenantId, tenantId).one();
        ServiceAssert.notNull(existRule, StatusCode.Common.NULL_ERROR.getCode(), "路由不存在");

        // 技能组校验
        checkSkillSetExist(tenantId, req.getRouteBizRuleList(), req.getQueueTimeoutCfg());

        // 服务时间组校验
        checkServiceTimeExist(tenantId, req.getBizTimeId(), req.getRouteBizRuleList());

        RouteDistributionRule rule = RouteDistributionRuleAssembler.INSTANCE.toRouteDistributionRule(req);
        this.updateById(rule);

        List<RouteBizRule> routeBizRuleList = RouteDistributionRuleAssembler.INSTANCE.toRouteBizRuleList(req.getRouteBizRuleList());
        routeBizRuleList.forEach(bizRule -> {
            bizRule.setRuleId(rule.getId());
        });
        routeBizRuleService.deleteByRuleId(req.getId());
        routeBizRuleService.batchInsert(routeBizRuleList);

        clearCache(rule.getId());
    }

    /**
     * 删除路由规则
     *
     * @param tenantId
     * @param req
     */
    public void delete(String tenantId, DeletingRouteRuleReqVO req) {
        if (CollectionUtils.isEmpty(req.getIdList())) {
            return;
        }
        this.lambdaUpdate().set(RouteDistributionRule::getDeleteFlag, YesOrNotEnum.YES.getValue())
                .set(RouteDistributionRule::getUpdateTime, new Date())
                .in(RouteDistributionRule::getId, req.getIdList())
                .eq(RouteDistributionRule::getTenantId, tenantId)
                .update();

        req.getIdList().forEach(this::clearCache);
    }

    /**
     * 开启或关闭路由
     *
     * @param tenantId
     * @param req
     */
    public void enable(String tenantId, UpdatingRouteRuleEnableReqVO req) {
        this.lambdaUpdate().set(RouteDistributionRule::getEnableFlag, req.getEnableFlag())
                .set(RouteDistributionRule::getUpdateTime, new Date())
                .eq(RouteDistributionRule::getId, req.getId())
                .eq(RouteDistributionRule::getTenantId, tenantId)
                .update();
        clearCache(req.getId());
    }

    /**
     * 获取详情
     *
     * @param tenantId
     * @param id
     * @return
     */
    public RouteDistributionRuleDetailRespVO detail(String tenantId, Integer id) {
        RouteDistributionRule rule = queryById(id);
        ServiceAssert.notNull(rule, StatusCode.Common.NULL_ERROR.getCode(), "路由不存在");
        ServiceAssert.isTrue(rule.getTenantId().equals(tenantId), StatusCode.Common.NULL_ERROR.getCode(), "路由不存在");
        return RouteDistributionRuleAssembler.INSTANCE.toRouteDistributionRuleDetailRespVO(rule);
    }

    /**
     * 查询分页
     *
     * @param req
     * @return
     */
    public PageResponse<RouteRulePageRespVO> queryPage(PageRequest<QueryingRouteRuleReqVO> req) {
        IPage<RouteDistributionRule> page = baseMapper.page(new Page<>(req.getPage(), req.getPageSize()), req.getData());

        List<Long> csIdList = page.getRecords().stream().map(RouteDistributionRule::getCreatorId).distinct().collect(Collectors.toList());
        Map<Long, CsMemberInfoDTO> memberInfoMap = customerServiceClient.queryByIdList(csIdList)
                .stream().collect(Collectors.toMap(CsMemberInfoDTO::getUserId, v -> v, (o, n) -> o));

        List<Integer> routeIdList = page.getRecords().stream().map(RouteDistributionRule::getId).collect(Collectors.toList());
        Map<Integer, List<ChannelInfoDTO>> channelInfoMap = customerServiceClient.queryChannelListBy(routeIdList);

        List<RouteRulePageRespVO> respVOList = page.getRecords().stream()
                .map(rule -> RouteRulePageRespVO.builder()
                        .id(rule.getId())
                        .name(rule.getName())
                        .enableFlag(rule.getEnableFlag())
                        .updateTime(rule.getUpdateTime())
                        .creatorName(Optional.ofNullable(memberInfoMap.get(rule.getCreatorId())).map(CsMemberInfoDTO::getNickName).orElse(StringUtils.EMPTY))
                        .configFlag(CollectionUtils.isNotEmpty(channelInfoMap.get(rule.getId())) ? YesOrNotEnum.YES.getValue() : YesOrNotEnum.NOT.getValue())
                        .build()).collect(Collectors.toList());

        return new PageResponse<>(page.getTotal(), req.getPageSize(), req.getPage(), respVOList);
    }

    /**
     * 校验技能组是否存在
     *
     * @param tenantId
     * @param routeBizRuleList
     * @param queueTimeoutCfg
     */
    private void checkSkillSetExist(String tenantId, List<RouteBizRuleReqVO> routeBizRuleList, RouteQueueOverflowCfgReqVO queueTimeoutCfg) {
        Map<Long, SkillSet> skillSetMap = skillSetService.getSkillSetMap(tenantId);
        routeBizRuleList.forEach(bizRule -> {
            for (String skillSetIdStr : bizRule.getSkillSetIds()) {
                Long skillSetId = Long.parseLong(skillSetIdStr);
                ServiceAssert.notNull(skillSetMap.get(skillSetId), StatusCode.Common.NULL_ERROR.getCode(), String.format("技能组id[%s]已被删除", skillSetIdStr));
            }
        });
        if (CollectionUtils.isNotEmpty(queueTimeoutCfg.getSkillSetIdList())) {
            for (Long skillSetId : queueTimeoutCfg.getSkillSetIdList()) {
                ServiceAssert.notNull(skillSetMap.get(skillSetId), StatusCode.Common.NULL_ERROR.getCode(), String.format("技能组id[%s]已被删除", skillSetId));
            }
        }
    }

    /**
     * 校验服务时间组是否存在
     *
     * @param tenantId
     * @param bizTimeId
     * @param routeBizRuleList
     */
    private void checkServiceTimeExist(String tenantId, Integer bizTimeId, List<RouteBizRuleReqVO> routeBizRuleList) {
        Map<Integer, ServiceTime> serviceTimeMap = serviceTimeService.getServiceTimeList(tenantId)
                .stream().collect(Collectors.toMap(ServiceTime::getId, v -> v, (o, n) -> o));
        ServiceAssert.notNull(serviceTimeMap.get(bizTimeId), StatusCode.Common.NULL_ERROR.getCode(), "路由配置的服务时间组已被删除");
        routeBizRuleList.forEach(bizRule -> {
            ServiceAssert.notNull(serviceTimeMap.get(bizRule.getBizTimeId()), StatusCode.Common.NULL_ERROR.getCode(), String.format("业务规则[%s]配置的服务时间组已被删除", bizRule.getName()));
        });
    }

    /**
     * 清理缓存
     *
     * @param id
     */
    private void clearCache(Integer id) {
        String cacheKey = String.format(CacheKeyUtil.CsrRoute.ROUTE_INFO, id);
        redissonClient.getBucket(cacheKey).deleteAsync();
    }


}
